﻿using SuperX.Common.Log;
using SuperX.SuperSocket.Client.UpLoad.Command;
using SuperX.SuperSocket.Common;
using SuperX.SuperSocket.Extensions;
using SuperSocket.ClientEngine;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using ErrorEventArgs = SuperSocket.ClientEngine.ErrorEventArgs;

namespace SuperX.SuperSocket.Client.UpLoad
{
    internal class UpLoadEngine : IDisposable
    {
        private FileUploadingEvents FileUploadingEvents;
        protected IClientCommandReader<TransferCommandInfo> CommandReader { get; private set; }
        private string _fileName;
        public string _saveName;
        private Dictionary<string, Extensions.ICommand<UpLoadEngine, TransferCommandInfo>> m_CommandDict = new Dictionary<string, Extensions.ICommand<UpLoadEngine, TransferCommandInfo>>(StringComparer.OrdinalIgnoreCase);
        private FileStream m_fileStream;
        private int PacketSize = 1024 * 40;
        private byte[] readBuffer;
        private long TransferedLength = 0L;
        internal int TransferStatus = 0;

        //0:初始化状态，1：CheckExist,3,OnUpLoad,开始上传，4，OnDoData，数据传输中，5，数据传输完成,6,已结束的标志
        private long TransferTotalLength = 0L;

        internal TcpClientSession Client;
        internal AutoResetEvent m_OpenedEvent = new AutoResetEvent(false);
        internal AutoResetEvent m_EndEvent = new AutoResetEvent(false);
        internal EndPoint Endpoint;

        public UpLoadEngine(FileUploadingEvents _FileUploadingEvents)
        {
            FileUploadingEvents = _FileUploadingEvents;
        }

        public void BeginUpLoad()
        {
            try
            {
                if (TransferStatus != 0) Logger.Error("Client：SuperSocket状态错误");
                Client.Connect(Endpoint);
                if (!m_OpenedEvent.WaitOne(5000)) Logger.Error("Client：SuperSocket连接失败，请检查服务器！！");
                SendMessage(Tag.CheckExist, _saveName);
            }
            catch (Exception e)
            {
                Logger.ErrorException(e);
                OnTransferError(new TransferErrorEventArgs(e));
            }
        }

        public void SendStr(string msg)
        {
            try
            {
                if (TransferStatus != 0) Logger.Error("Client：SuperSocket状态错误");
                Client.Connect(Endpoint);
                if (!m_OpenedEvent.WaitOne(5000)) Logger.Error("Client：SuperSocket连接失败，请检查服务器！");
                SendMessage(Tag.Receiving, msg);
            }
            catch (Exception e)
            {
                Logger.ErrorException(e);
                OnTransferError(new TransferErrorEventArgs(e));
            }
        }

        internal void BeginUpLoad(ref string fileId, ref string AddId)
        {
            BeginUpLoad();
            fileId = string.Empty;
            AddId = string.Empty;
        }

        private void client_Closed(object sender, EventArgs e)
        {
            TransferStatus = 6;
            DoEnd();
        }

        private void client_Connected(object sender, EventArgs e)
        {
            m_OpenedEvent.Set();
        }

        private void client_DataReceived(object sender, DataEventArgs e)
        {
            m_OnDataReceived(e.Data, e.Offset, e.Length);
        }

        private void client_Error(object sender, ErrorEventArgs e)
        {
        }

        public void Dispose()
        {
            _fileName = string.Empty;
            _saveName = string.Empty;
            if (m_fileStream != null)
            {
                m_fileStream.Close();
                m_fileStream = null;
            }
            TcpClientSession client = Client;
            if (client != null)
            {
                client.Error -= new EventHandler<ErrorEventArgs>(client_Error);
                client.DataReceived -= new EventHandler<DataEventArgs>(client_DataReceived);
                client.Connected -= new EventHandler(client_Connected);
                client.Closed -= new EventHandler(client_Closed);
                if (client.IsConnected)
                {
                    client.Close();
                }
                Client = null;
            }
            //this.TransferStatus = 0;
            TransferedLength = 0L;
            TransferTotalLength = 0L;
            readBuffer = null;
            CommandReader = null;
            m_CommandDict.Clear();
            m_OpenedEvent.Reset();
            m_EndEvent.Reset();
        }

        internal void DoData()
        {
            TransferStatus = 4;
            while (TransferedLength < TransferTotalLength)
            {
                if (TransferStatus == 6)
                {
                    if (Client != null && !Client.IsConnected)
                    {
                        OnTransferError(new TransferErrorEventArgs(new Exception("服务端关闭")));
                        return;
                    }
                    else
                    {
                        DoEnd();
                        return;
                    }
                }
                int length = m_fileStream.Read(readBuffer, 0, PacketSize);
                SendData(Tag.DoData, readBuffer, 0, length);
                TransferedLength += length;
                OnTransferStep(TransferTotalLength, TransferedLength, length);
            }
            TransferStatus = 5;
            //结束命令
            SendData(Tag.DoEnd, new byte[] { 1 }, 0, 1);
            //停止方法
            //DoEnd();
        }

        internal void DoEnd()
        {
            Logger.Trace("Client：客户端断开连接！");
            TransferStatus = 6;
            Dispose();
            if (FileUploadingEvents.OnTransferComplete != null)
                FileUploadingEvents.OnTransferComplete(new EventArgs());
        }

        //客户端自己触发结束事件
        //internal void OnDoEnd()
        //{
        //    DoEnd();
        //}

        internal void DoUpLoad()
        {
            m_fileStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
            TransferTotalLength = m_fileStream.Length;
            UpLoadInfo t = new UpLoadInfo(_fileName, m_fileStream.Length, _SaveName: _saveName);
            byte[] data = SerializeHelp.Serialize(t);
            SendData(Tag.DoUpLoad, data, 0, data.Length);
        }

        private void ExecuteCommand(TransferCommandInfo commandInfo)
        {
            Extensions.ICommand<UpLoadEngine, TransferCommandInfo> command;
            if (m_CommandDict.TryGetValue(commandInfo.Key, out command))
            {
                command.ExecuteCommand(this, commandInfo);
            }
        }

        public void Init(EndPoint endpoint, string fileName, string saveName)
        {
            Endpoint = endpoint;
            string str = ConfigurationManager.AppSettings["PacketSize"];
            if (!string.IsNullOrWhiteSpace(str))
            {
                PacketSize = Convert.ToInt32(str);
            }
            readBuffer = new byte[PacketSize];
            _fileName = fileName;
            _saveName = saveName;
            UpLoadCommandBase[] baseArray = new UpLoadCommandBase[] { new CheckExist(), new DoData(), new DoEnd() };
            foreach (UpLoadCommandBase base2 in baseArray)
            {
                m_CommandDict.Add(base2.Name, base2);
            }
            TcpClientSession session = new AsyncTcpSession();
            session.Error += new EventHandler<ErrorEventArgs>(client_Error);
            session.DataReceived += new EventHandler<DataEventArgs>(client_DataReceived);
            session.Connected += new EventHandler(client_Connected);
            session.Closed += new EventHandler(client_Closed);
            Client = session;
            CommandReader = new UpLoadReader(this);
        }

        private void m_OnDataReceived(byte[] data, int offset, int length)
        {
            while (true)
            {
                if (CommandReader == null)
                    return;
                int num;
                TransferCommandInfo commandInfo = CommandReader.GetCommandInfo(data, offset, length, out num);
                if (CommandReader.NextCommandReader != null)
                {
                    CommandReader = CommandReader.NextCommandReader;
                }
                if (commandInfo != null)
                {
                    ExecuteCommand(commandInfo);
                }
                if (num <= 0)
                {
                    return;
                }
                offset = offset + length - num;
                length = num;
            }
        }

        internal bool OnFileExist()
        {
            if (FileUploadingEvents.OnFileExist != null)
            {
                FileUploadingEvents.OnFileExist(new EventArgs());
            }
            OnTransferError(new TransferErrorEventArgs(new Exception("存在相同文件")));
            return false;
        }

        internal void OnTransferError(TransferErrorEventArgs e)
        {
            if (FileUploadingEvents.OnTransferError != null)
            {
                FileUploadingEvents.OnTransferError(e);
            }
            Dispose();
        }

        internal void OnTransferStep(long _TotalLen, long _TransferLen, int _CurrentPacket)
        {
            if (FileUploadingEvents.OnTransferStep != null)
            {
                FileUploadingEvents.OnTransferStep(new TransferStepEventArgs(_TotalLen, _TransferLen, _CurrentPacket));
            }
        }

        internal void SendData(Tag opCode, byte[] data, int offset, int length)
        {
            byte[] senddata = new byte[length + 5];
            senddata[0] = 0;
            senddata[1] = (byte)((int)opCode / 256);
            senddata[2] = (byte)((int)opCode % 256);
            senddata[3] = (byte)(length / 256);
            senddata[4] = (byte)(length % 256);
            Buffer.BlockCopy(data, offset, senddata, 5, length);
            Client.Send(senddata, 0, senddata.Length);
        }

        internal void SendMessage(Tag opCode, string msg)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(msg);
            SendData(opCode, bytes, 0, bytes.Length);
        }
    }

    internal class FileUploadingEvents : IFileUploadingEvents
    {
        internal BTransferEventHandler<EventArgs> OnFileExist;

        internal TransferEventHandler<EventArgs> OnTransferComplete;

        internal TransferEventHandler<TransferErrorEventArgs> OnTransferError;

        internal TransferEventHandler<TransferStepEventArgs> OnTransferStep;

        public event BTransferEventHandler<EventArgs> FileExist
        {
            add { OnFileExist = (BTransferEventHandler<EventArgs>)Delegate.Combine(OnFileExist, value); }
            remove { OnFileExist = (BTransferEventHandler<EventArgs>)Delegate.Remove(OnFileExist, value); }
        }

        public event TransferEventHandler<EventArgs> TransferComplete
        {
            add { OnTransferComplete = (TransferEventHandler<EventArgs>)Delegate.Combine(OnTransferComplete, value); }
            remove { OnTransferComplete = (TransferEventHandler<EventArgs>)Delegate.Remove(OnTransferComplete, value); }
        }

        public event TransferEventHandler<TransferErrorEventArgs> TransferError
        {
            add { OnTransferError = (TransferEventHandler<TransferErrorEventArgs>)Delegate.Combine(OnTransferError, value); }
            remove { OnTransferError = (TransferEventHandler<TransferErrorEventArgs>)Delegate.Remove(OnTransferError, value); }
        }

        public event TransferEventHandler<TransferStepEventArgs> TransferStep
        {
            add { OnTransferStep = (TransferEventHandler<TransferStepEventArgs>)Delegate.Combine(OnTransferStep, value); }
            remove { OnTransferStep = (TransferEventHandler<TransferStepEventArgs>)Delegate.Remove(OnTransferStep, value); }
        }
    }
}